#include "chatservice.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h>
#include <vector>

using namespace std;
using namespace muduo;
using namespace muduo::net;

ChatService *ChatService::getInstance()
{
    static ChatService server;
    return &server;
}

ChatService::ChatService()
{
    _msgHandlerMap.insert(std::make_pair(static_cast<int>(MsgType::LOGIN_MSG), std::bind(&ChatService::login, this, _1, _2, _3)));
    _msgHandlerMap.insert(std::make_pair(static_cast<int>(MsgType::REG_MSG), std::bind(&ChatService::reg, this, _1, _2, _3)));
    _msgHandlerMap.insert(std::make_pair(static_cast<int>(MsgType::ONE_CHAT_MSG), std::bind(&ChatService::oneChat, this, _1, _2, _3)));
    _msgHandlerMap.insert(std::make_pair(static_cast<int>(MsgType::ADD_FRIEND_MSG), std::bind(&ChatService::addFriend, this, _1, _2, _3)));
    _msgHandlerMap.insert(std::make_pair(static_cast<int>(MsgType::CREATE_GROUP_MSG), std::bind(&ChatService::createGroup, this, _1, _2, _3)));
    _msgHandlerMap.insert(std::make_pair(static_cast<int>(MsgType::LOGINOUT_MSG), std::bind(&ChatService::loginout, this, _1, _2, _3)));
    _msgHandlerMap.insert(std::make_pair(static_cast<int>(MsgType::ADD_GROUP_MSG), std::bind(&ChatService::addGroup, this, _1, _2, _3)));
    _msgHandlerMap.insert(std::make_pair(static_cast<int>(MsgType::GROUP_CHAT_MSG), std::bind(&ChatService::groupChat, this, _1, _2, _3)));

    if (_redis.connect())
    {
        LOG_INFO << "redis connect success";
        _redis.init_notify_handler(std::bind(&ChatService::handleRedisSubscribeMessage, this, _1, _2));
    }
}

// 从redis消息队列中获取订阅的消息
void ChatService::handleRedisSubscribeMessage(int userid, string msg)
{
    lock_guard<mutex> lock(_connMutex);
    auto it = _userConnMap.find(userid);
    if (it != _userConnMap.end())
    {
        it->second->send(msg);
        return;
    }

    // 存储该用户的离线消息
    _offLineMsgModel.insert(userid, msg);
}
// 退出登录业务
void ChatService::loginout(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int id = js["id"].get<int>();

    // 在map中删除conn
    {
        lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(id);
        if (it != _userConnMap.end())
        {
            _userConnMap.erase(it);
        }
    }
    // 在消息队列中取消该用户的订阅
    _redis.unsubscribe(id);
    User user;
    user.setId(id);
    // 退出登录，把状态改为 offline
    user.setState("offline");
    _userModel.updateStatu(user);
}
// 处理登录业务 id password
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int id = js["id"].get<int>();
    string password = js["password"].get<string>();

    User user;
    user.setId(id);
    user.setPassword(password);

    user = _userModel.query(id);
    if (user.getId() == id && user.getPassword() == password)
    {
        if (user.getState() == "online")
        {
            // 该用户已经登录，不允许重复登录
            json response;
            response["msgid"] = static_cast<int>(MsgType::LOGIN_MSG_ACK);
            response["errno"] = 2; // 0 代表没错
            response["errmsg"] = "this account is using ,input another account";
            conn->send(response.dump()); // 发送数据？
        }
        else
        {
            // id用户登录成功之后，订阅该用户的消息
            _redis.subscribe(id);
            // 登录成功
            // 把状态改为 online
            user.setState("online");
            _userModel.updateStatu(user);

            {
                lock_guard<mutex> lock(_connMutex);
                // 讲用户信息放入连接
                _userConnMap.insert({id, conn});
            }

            json response;
            response["msgid"] = static_cast<int>(MsgType::LOGIN_MSG_ACK);
            response["errno"] = 0; // 0 代表没错
            response["id"] = user.getId();
            response["name"] = user.getName();

            // 查询该用户是否有离线消息
            // 用户登录成功了，把离线消息发送给用户
            LOG_INFO << user.getId();
            vector<string> vec = _offLineMsgModel.query(user.getId());
            if (!vec.empty())
            {
                // 如果存在离线消息，发送给用户
                response["offlinemsg"] = vec;
                // 读取到离线消息的，后腰删除
                _offLineMsgModel.remove(id);
            }

            vector<User> vec2 = _friendModel.getFriendList(user.getId());
            if (!vec2.empty())
            {
                vector<string> vec3;
                for (auto &user : vec2)
                {
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    vec3.push_back(js.dump());
                }
                response["friends"] = vec3;
            }
            // 查询用户的群组信息
            vector<Group> groupuserVec = _groupModel.queryGroups(id);
            if (!groupuserVec.empty())
            {
                // group:[{groupid:[xxx, xxx, xxx, xxx]}]
                vector<string> groupV;
                for (Group &group : groupuserVec)
                {
                    json grpjson;
                    grpjson["id"] = group.getId();
                    grpjson["groupname"] = group.getName();
                    grpjson["groupdesc"] = group.getDesc();
                    vector<string> userV;
                    for (GroupUser &user : group.getUsers())
                    {
                        json js;
                        js["id"] = user.getId();
                        js["name"] = user.getName();
                        js["state"] = user.getState();
                        js["role"] = user.getRole();
                        userV.push_back(js.dump());
                    }
                    grpjson["users"] = userV;
                    groupV.push_back(grpjson.dump());
                }

                response["groups"] = groupV;
            }

            conn->send(response.dump()); // 发送数据？
        }
    }
    else
    {
        // 登录失败
        json response;
        response["msgid"] = static_cast<int>(MsgType::LOGIN_MSG_ACK);
        response["errno"] = 1; // 0 代表没错
        response["errmsg"] = "id or name is wrong";
        conn->send(response.dump()); // 发送数据？
    }
}
// name password
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    string name = js["name"].get<string>();
    string password = js["password"].get<string>();

    User user;
    user.setName(name);
    user.setPassword(password);
    // 这里是引用传递
    bool state = _userModel.insert(user);
    if (state)
    {
        // 注册成功
        json response;
        response["msgid"] = static_cast<int>(MsgType::REG_MSG_ACK);
        response["errno"] = 0; // 0 代表没错
        response["id"] = user.getId();
        conn->send(response.dump()); // 发送数据？
    }
    else
    {
        // 注册失败
        json response;
        response["msgid"] = static_cast<int>(MsgType::REG_MSG_ACK);
        response["errno"] = 1; // 0 代表没错
        response["id"] = user.getId();
        conn->send(response.dump()); // 发送数据？
    }
}

/// @brief 根据消息id 得到对应的处理方法
/// @param msgid
/// @return
msgHandler ChatService::getMsgHandler(int msgid)
{
    auto it = _msgHandlerMap.find(msgid);
    if (it == _msgHandlerMap.end())
    {
        // 返回一个默认处理起
        return [=](const TcpConnectionPtr &conn, json &js, Timestamp time)
        {
            LOG_INFO << "msgid:" << msgid << "can not find handler";
        };
    }
    return _msgHandlerMap[msgid];
}

void ChatService::clientCloseException(const TcpConnectionPtr &conn)
{
    // 要做两件事 ，一是在map中删除，二是在数据库中改变状态
    User user;
    {
        lock_guard<mutex> lock(_connMutex);

        for (auto it = _userConnMap.begin(); it != _userConnMap.end(); it++)
        {
            if (conn == it->second)
            {
                user.setId(it->first);
                _userConnMap.erase(it);
                break;
            }
        }
    }
    // 用户注销，就相当于下线，在redis中取消订阅
    _redis.unsubscribe(user.getId());

    if (user.getId() != -1)
    {
        user.setState("offline");
        // 从数据库中把这个用户的状态改为 offline
        _userModel.updateStatu(user);
    }
}

// 一对一聊天业务
void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int toid = js["toid"].get<int>();
    // 标识用户是否在线

    {
        lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(toid);
        if (it != _userConnMap.end())
        {
            // 证明找到了 转发消息
            // conn 服务器做了一次消息中转
            it->second->send(js.dump());
            return;
        }
    }
    // 查询toid是否在线
    User user = _userModel.query(toid);
    if (user.getState() == "online")
    {
        // 用户在线，转发消息
        _redis.publish(toid, js.dump());
        return;
    }
    // 不在线，存储离线消息
    // LOG_INFO << js.dump();
    _offLineMsgModel.insert(toid, js.dump());
}

void ChatService::reset()
{
    _userModel.resetStatu();
}

/// @brief msgid id friendid
/// @param conn
/// @param js
/// @param time
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int friendid = js["friendid"].get<int>();

    // 关系写到数据库中
    _friendModel.insert(userid, friendid);
}
#include <iostream>
using namespace std;
// id name desc
void ChatService::createGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    string name = js["groupname"].get<string>();
    // cout<<name<<endl;;
    string desc = js["groupdesc"].get<string>();
    // cout<<desc<<endl;;
    // 这里第一个参数为什么设置为-1 ，没有实际意义，只是为了方便
    Group group(-1, name, desc);
    if (_groupModel.createGroup(group))
    {
        // 创建成功，把创建的这个人当作第一个成员，并设置为群主
        _groupModel.addGroup(group.getId(), userid, "creator");
    }
}

// id groupid desc
void ChatService::addGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    _groupModel.addGroup(groupid, userid, "normal");
}

void ChatService::groupChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    // 得到群聊的每一个成员的id
    int userid = js["userid"].get<int>();
    int groupid = js["groupid"].get<int>();
    vector<int> useridVec = _groupModel.queryGroupsUser(userid, groupid);

    lock_guard<mutex> lock(_connMutex);
    // 根据id 得到conn
    for (auto id : useridVec)
    {

        auto it = _userConnMap.find(id);
        if (it != _userConnMap.end())
        {
            // 转发消息
            it->second->send(js.dump());
        }
        else
        {
            User user = _userModel.query(id);
            if (user.getState() == "online")
            {
                // 用户在线，转发消息
                _redis.publish(id, js.dump());
            }
            else
            {
                // 将信息作为离线的存储起来
                _offLineMsgModel.insert(id, js.dump());
            }
        }
    }
}